﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CharmL.Helper;

namespace CharmL {

    public class LatticeNode {
        public class LatticeNodeComparer : Comparer<LatticeNode> {

            public override int Compare(LatticeNode x, LatticeNode y) {
                return LatticeNode.CmpFreqIt(x, y);
            }
        }



        public List<int> Items { get; set; }
        public int Sup { get; set; }
        public long Flag { get; set; }
        public List<LatticeNode> Parent { get; set; }
        public List<LatticeNode> Child { get; set; }
        public List<List<int>> MinGen { get; set; }


        public LatticeNode() {

            Items = new List<int>();
            Parent = new List<LatticeNode>();
            Child = new List<LatticeNode>();
            MinGen = new List<List<int>>();
            Sup = 0;
            Flag = 0;
        }

        public LatticeNode(LatticeNode node)
            : this() {
            Sup = node.Sup;
            Items = new List<int>(node.Items);
            //            LatticeNode::LatticeNode(LatticeNode *fit)
            //{
            //   (*(vector<int> *)this) = *((vector<int> *)fit);
            //   //this(*(vector<int> *)fit);
            //   _sup = fit->_sup;
            //   //cout << "COPY " << fit->seqcnt << " " << fit->seqsz << " " <<
            //   //   seqcnt << " " << seqsz << endl << flush;
            //}
        }

        public LatticeNode(List<int> vec, int sup)
            : this() {
            //TODO copy or assign
            //(*(vector<int>*)this) = *((vector<int>*)fit);            
            //_sup = fit->_sup;
            Sup = sup;
            Items = new List<int>(vec);
        }

        public void Add(int id) {
            //Items.Add(id);
            Items.AddWithOrder(id);
        }

        public void Sort(bool childParentSort) {
            Items.Sort();
            if (childParentSort) {
                Parent.Sort(new LatticeNodeComparer());
                Child.Sort(new LatticeNodeComparer());
            }
            //              void LatticeNode::sort(bool childpar_sort)
            //{
            //   std::sort(begin(), end());
            //   if (childpar_sort){
            //      _child->sort(cmpless);
            //      _parent->sort(cmpless);
            //   }
            //}
        }

        public void Print() {
            Console.WriteLine(string.Join(" ", Items));
        }

        public void Print(StreamWriter writer) {
            //TODO
            //ostream& operator << (ostream& outputStream, LatticeNode& freq)
            //{
            //   //outputStream << "FREQ : ";
            //   //   outputStream << static_cast<vector<int>&> (freq);
            //   for (int i=0; i < freq.size(); ++i)
            //      outputStream << freq[i] << " ";   
            //   //outputStream << Dbase_Ctrl_Blk::FreqIdx[freq[i]] << " ";   
            //   outputStream << "( " << freq._sup << " )";
            //   return outputStream;
            //}
        }

        public static int CmpFreqIt(LatticeNode x, LatticeNode y) {
            int res = 0;
            if (x.Items.Count > x.Items.Count) res = 1;
            else if (x.Items.Count < y.Items.Count) res = -1;

            if (res == 0) {
                for (int i = 0; i < x.Items.Count; i++) {
                    if (x.Items[i] < y.Items[i]) res = -1;
                    else if (x.Items[i] > y.Items[i]) res = 1;
                    if (res != 0) break;
                }
            }
            //vector<int>* fit1 = *(vector<int>**)f1;
            //vector<int>* fit2 = *(vector<int>**)f2;
            ////compare size
            //if (fit1->size() > fit2->size()) res = 1;
            //else if (fit1->size() < fit2->size()) res = -1;


            //if (res == 0) {
            //    //compare items
            //    for (i = 0; i < fit1->size(); i++) {

            //        if ((*fit1)[i] < (*fit2)[i]) res = -1;
            //        else if ((*fit1)[i] > (*fit2)[i]) res = 1;
            //        if (res != 0) break;
            //    }
            //}

            return res;
        }


        public int Compare(LatticeNode node) {
            return CmpFreqIt(this, node);
        }

        public void SubDiff(LatticeNode par, LatticeNode diff) {
            diff.Items = new List<int>();
            int i, j;
            for (i = 0, j = 0; i < Items.Count && j < par.Items.Count; ) {
                if (Items[i] > par.Items[j]) {
                    diff.Items.Add(par.Items[j]);
                    j++;
                } else if (Items[i] < par.Items[j]) {
                    i++;
                } else {
                    i++;
                    j++;
                }
            }
            for (; j < par.Items.Count; j++)
                diff.Items.Add(par.Items[j]);


            //this is a subset of par; return the difference par-this
            //void LatticeNode::subdiff(LatticeNode *par, LatticeNode *diff){
            //  diff->reset();
            //  int i, j;

            //  for (i=0, j=0; i < size() && j < par->size() ;){
            //    if ((*this)[i] > (*par)[j]){
            //      diff->add((*par)[j]);
            //      j++; 
            //    } 
            //    else if ((*this)[i] < (*par)[j]){
            //      i++;
            //    }
            //    else{
            //      i++;
            //      j++;
            //    }
            //  }
            //  for (; j < par->size(); j++)
            //    diff->add((*par)[j]);
            //}
        }

        public SubSetRes SubSet(LatticeNode set) {
            int i, j;
            int di = 0, dj = 0;

            for (i = 0, j = 0; i < Items.Count && j < set.Items.Count; ) {
                if (Items[i] > set.Items[j]) {
                    j++;
                    dj++;
                } else if (Items[i] < set.Items[j]) {
                    i++;
                    di++;
                } else {
                    i++;
                    j++;
                }
            }
            if (i < Items.Count) di++;
            if (j < set.Items.Count) dj++;

            if (di == 0 && dj == 0) return SubSetRes.EQUAL;
            else if (di == 0 && dj > 0) return SubSetRes.SUBSET;
            else if (di > 0 && dj == 0) return SubSetRes.SUPSET;
            else return SubSetRes.NEQUAL;

            //   int i,j;
            //   int di=0, dj=0;


            //      for (i=0, j=0; i < size() && j < set->size() ;){
            //         if ((*this)[i] > (*set)[j]){
            //            j++;
            //            dj++;
            //         }
            //         else if ((*this)[i] < (*set)[j]){
            //            i++;
            //            di++;
            //         }
            //         else{
            //            i++;
            //            j++;
            //         }
            //      }


            //      if (i < size()) di++;
            //      if (j < set->size()) dj++;

            //      if (di == 0 && dj == 0) return EQUAL;
            //      else if (di == 0 && dj > 0) return SUBSET;
            //      else if (di > 0 && dj == 0) return SUPSET;
            //      else return NEQUAL;
        }

        public void PrintNode(StreamWriter writer, bool printMinGen = false) {
            //TODO
            //void LatticeNode::print_node(ostream &out, bool print_mingen){
            //   list<LatticeNode *>::iterator i;
            //   list<vector<int> >::iterator j;

            //out << "NODE : " << *this << endl;
            writer.WriteLine("Items : " + string.Join(" ", Items));
            //   out << "PARENTS : ";
            writer.WriteLine("Parents : ");

            for (int i = 0; i < this.Parent.Count; i++) {
                Parent[i].PrintNode(writer, printMinGen);
            }

            for (int i = 0; i < this.Child.Count; i++) {
                Child[i].PrintNode(writer, printMinGen);
            }
            //for (int i = 0; i < freq.size(); ++i)
            //    outputStream << freq[i] << " ";

            //outputStream << "( " << freq._sup << " )";
            //return outputStream;

            //   //parent()->sort(LatticeNode::cmpless);
            //   for (i = parent()->begin(); i!= parent()->end(); i++){
            //      out << *(*i) << " , ";
            //   }
            //   out << endl;

            //   out << "CHILDREN : ";
            //   //child()->sort(LatticeNode::cmpless);
            //   for (i = child()->begin(); i!= child()->end(); i++){
            //      out << *(*i) << " , ";
            //   }
            //   out << endl;

            //   if (print_mingen){
            //      out << "MINGENS : ";
            //      for (j = mingen()->begin(); j!= mingen()->end(); ++j){
            //         out << (*j) << " , ";
            //      }
            //      out << endl;
            //   }

            //}
        }

        public void PrintLattice(StreamWriter writer, LatticeNode node, long marker) {
            //TOCHECK finish translate to C#
            if (node.Flag == marker) return;
            node.Flag = marker;
            node.PrintNode(writer);
            foreach (LatticeNode childNode in Child) {
                PrintLattice(writer, childNode, marker);
            }
            //void LatticeNode::print_lattice(LatticeNode *node, long marker)
            //{
            //   list<LatticeNode *>::iterator i;
            //   if (node->flag() == marker) return;

            //   node->flag() = marker;

            //   node->print_node(cout);

            //   for (i = node->child()->begin(); i!= node->child()->end(); i++){
            //      print_lattice(*i, marker);
            //   }
            //}
        }

        public LatticeNode AddChild(List<int> vec, int sup) {
            LatticeNode ln = new LatticeNode(vec, sup);
            Child.Add(ln);
            ln.Parent.Add(this);
            ln.Items.Sort();

            return ln;
            //LatticeNode * LatticeNode::add_child(vector<int> &vec, int sup)
            //{
            //   LatticeNode *ln = new LatticeNode(vec, sup);
            //   _child->push_back(ln);
            //   ln->_parent->push_back(this);

            //   std::sort(ln->begin(), ln->end(), less<int>());
            //   return ln;
            //}
        }

        public LatticeNode AddChild(LatticeNode ln) {
            if (!Child.Contains(ln) && this != ln) {
                Child.Add(ln);
                ln.Parent.Add(this);
            }
            return ln;
            //LatticeNode * LatticeNode::add_child(LatticeNode *ln)
            //{
            //   _child->push_back(ln);
            //   ln->_parent->push_back(this);
            //   return ln;
            //}
        }

        public LatticeNode AddChild(List<int> vec, int litem, int sup) {
            //LatticeNode * LatticeNode::add_child(vector<int> &vec, int litem, int sup)
            //{
            //   LatticeNode *ln = new LatticeNode(vec, sup);
            //   ln->push_back(litem);
            //   _child->push_back(ln);
            //   ln->_parent->push_back(this);
            //   std::sort(ln->begin(), ln->end(), less<int>());
            //   return ln;
            //}
            LatticeNode ln = new LatticeNode(vec, sup);
            ln.Items.Add(litem);
            if (!Child.Contains(ln)) {
                Child.Add(ln);
                ln.Parent.Add(this);
            }
            ln.Items.Sort();
            return ln;

        }
        //public static bool operator != (LatticeNode n1, LatticeNode n2) {
        //    //if (n2 is null && n1 == null) return false;
        //    if (CmpFreqIt(n1, n2) == 0) return false;
        //    else return true;

        //}

        //public static bool operator ==(LatticeNode n1, LatticeNode n2) {
        //    if (n1 == null && n2 == null) return true;
        //    if (CmpFreqIt(n1, n2) == 0) return true;
        //    else return false;                        
        //}

        public void RemoveChildBiDirection(LatticeNode ln) {
            for (int i = 0; i < Child.Count; i++) {
                if (Child[i] == ln) {

                    for (int j = 0; j < ln.Parent.Count; j++) {
                        if (ln.Parent[j] == this) {
                            ln.Parent.RemoveAt(j);
                            break;
                        }
                    }
                    Child.RemoveAt(i);
                    return;
                }
            }
        }

        public void RemoveChild(LatticeNode ln) {
            // for (int i = Child.Count; i >= 0; i--) {
            for (int i = 0; i < Child.Count; i++) {
                if (Child[i] == ln) {
                    Child.RemoveAt(i);
                    return;
                }
            }
            //void LatticeNode::remove_child(LatticeNode *ln)
            //{
            //   list<LatticeNode *>::iterator i;
            //   for (i = _child->begin(); i!= _child->end(); ++i){
            //      if (*i == ln){
            //         i = _child->erase(i);
            //         break;
            //      }
            //   }
            //   //cout << "AFTERC :";
            //   //print_node();
            //}
        }

        public void RemoveParentBiDirection(LatticeNode ln) {
            for (int i = 0; i < Parent.Count; i++) {
                if (Parent[i] == ln) {
                    for (int j = 0; j < ln.Child.Count; j++) {
                        if (ln.Child[j] == this) {
                            ln.Child.RemoveAt(j);
                            break;
                        }
                    }
                    Parent.RemoveAt(i);

                    return;
                }
            }
        }

        public void RemoveParent(LatticeNode ln) {            
            for (int i = 0; i < Parent.Count; i++) {
                if (Parent[i] == ln) {
                    Parent.RemoveAt(i);

                    return;
                }
            }
        }

        public void MarkParents(long marker) {

            //void  LatticeNode::mark_parents(long marker)
            //{
            //   if (_flg == marker) return;
            //   list<LatticeNode *>::iterator i;
            //   for (i = _parent->begin(); i!= _parent->end(); ++i){
            //      (*i)->mark_parents(marker);
            //      (*i)->flag() = marker;
            //   }

            //}
            if (Flag == marker) return;
            for (int i = 0; i < Parent.Count; i++) {
                Parent[i].MarkParents(marker);
                Parent[i].Flag = marker;
            }
        }

        public void MarkChildren(long marker) {
            if (Flag == marker) return;
            for (int i = 0; i < Child.Count; i++) {
                Child[i].MarkChildren(marker);
                Child[i].Flag = marker;
            }
            //void  LatticeNode::mark_children(long marker)
            //{
            //   if (_flg == marker) return;
            //   list<LatticeNode *>::iterator i;
            //   for (i = _child->begin(); i!= _child->end(); ++i){
            //       (*i)->mark_children(marker);
            //       (*i)->flag() = marker;
            //   }

            //}

        }
    }
}